#include "../config.h"

#include "log.h"
#include "application.h"
#include "search.h"
#include "loader.h"

namespace gpyp {

namespace normal_re {
#include "normal_re/table_string.h"

RefRegex create(const Glib::ustring str) {
  Glib::ustring res;
  const char *c, *l, *e = str.c_str() + str.bytes();
  for(c = l = str.c_str(); c < e;) {
    for(ssize_t ti = 0;
        c < e && ti < static_cast<ssize_t>(table_size_); ++ti) {
      table_t *table = table_ + ti;
      for(size_t si = 0; si < max_size_; ++si) {
        const char *str = table->str[si];
        if (!str) break;
        if (strncmp(c, str, strlen(str)) == 0) {
          if (l < c) {
            res += Regex::escape(Glib::ustring(l, c));
          }
          Glib::ustring pat;
          for(size_t i = 0; i < max_size_; ++i) {
            if (!table->str[i]) {
              break;
            }
            if (!pat.empty()) pat += "|";
            pat += Regex::escape(table->str[i]);
          }
          res += Glib::ustring("(?:") + pat + ")";
          c = l = c + strlen(str);
          ti = -1;
          break;
        }
      }
    }
    c = g_utf8_find_next_char(c, NULL);
    if (!c) {
      res += Regex::escape(l);
      break;
    }
  }
  return Regex::create(res);
}
}/*normal_re*/

//========================
// AttributeSearchType
AttributeSearchType::NameTable AttributeSearchType::_name_table[] = {
  {SEARCHTYPE_NORMAL, "normal"},
  {SEARCHTYPE_MIGEMO, "migemo"},
  {SEARCHTYPE_REGEX,  "regex"},
  {SEARCHTYPE_NORMAL, NULL},
};

SearchType AttributeSearchType::next() {
  int res = static_cast<int>(get_value());
  ++res;
  if (!Search::migemo_usable() && res == SEARCHTYPE_MIGEMO) {
    ++res;
  }
  if (res > SEARCHTYPE_REGEX) {
    res = SEARCHTYPE_NORMAL;
  }
  return static_cast<SearchType>(res);
}

//========================
// AttributeMigemoMode
AttributeMigemoMode::NameTable AttributeMigemoMode::_name_table[] = {
  {MIGEMO_MODE_DISABLED,  "disabled"},
  {MIGEMO_MODE_CMIGEMO,   "cmigemo"},
  {MIGEMO_MODE_MIGEMOSRV, "migemosrv"},
  {MIGEMO_MODE_DISABLED,  NULL}
};

//========================
// Search
sigc::signal<void> Search::_signal_enable_migemo_changed;

Conf *Search::_conf = NULL;

MigemoMode Search::_migemo_mode = MIGEMO_MODE_DISABLED;
#ifdef USE_MIGEMO
migemo *Search::_migemo_obj = NULL;
#endif

void Search::init(Application &app) {
  _conf = &app.conf();

  _migemo_mode = *_conf->migemo()->mode();
  switch(_migemo_mode) {
  case MIGEMO_MODE_DISABLED: break;
  case MIGEMO_MODE_CMIGEMO:
#ifdef USE_MIGEMO
    if (!_migemo_obj) {
      log::log << "migemo: initializing...";
      const Glib::ustring &dict = *_conf->migemo()->dict_path();
      log::log << log::endl;
      if (Glib::file_test(dict, Glib::FILE_TEST_IS_REGULAR)) {
        _migemo_obj = migemo_open(dict.c_str());
        if (_migemo_obj && !migemo_is_enable(_migemo_obj)) {
          migemo_close(_migemo_obj);
          _migemo_obj = NULL;
        }
      } else {
        log::err << dict << " is not an regular file" << log::endl;
      }
      if (_migemo_obj) {
        if (!migemo_set_operator(_migemo_obj, MIGEMO_OPINDEX_NEST_IN,
                                 (const unsigned char*)"(?:")) {
          log::err << "migemo: set operator failed - "
            "MIGEMO_OPINDEX_NEST_IN=\"(?:\"" << log::endl;
        }
        const Glib::ustring &user_dict = *_conf->migemo()->dict_path_user();
        if (Glib::file_test(user_dict, Glib::FILE_TEST_IS_REGULAR)) {
          log::log << "migemo: user dictionary ditected" << log::endl;
          if (migemo_load(_migemo_obj, MIGEMO_DICTID_MIGEMO,
                          user_dict.c_str()) != MIGEMO_DICTID_MIGEMO) {
            log::err << "migemo: user dictionary load failed" << log::endl;
          }
        }
      } else {
        log::err << "migemo: failed to initialize" << log::endl;
      }
    }
#endif
    break;
  case MIGEMO_MODE_MIGEMOSRV:
    break;
  }
  _signal_enable_migemo_changed();
}
void Search::uninit() {
#ifdef USE_MIGEMO
  if (_migemo_obj) {
    migemo_close(_migemo_obj);
    _migemo_obj = NULL;
    _signal_enable_migemo_changed();
  }
#endif
}
bool Search::migemo_usable() {
  switch(_migemo_mode) {
  case MIGEMO_MODE_DISABLED: break;
  case MIGEMO_MODE_CMIGEMO:
#ifdef USE_MIGEMO
    return _migemo_obj != NULL;
#else
    break;
#endif
  case MIGEMO_MODE_MIGEMOSRV: return true;
  }
  return false;
}

Glib::ustring Search::migemo_query(const Glib::ustring &str) {
  Glib::ustring res;
  if (!_conf || str.length() < *_conf->migemo()->min_length()) {
    return res;
  }
  switch(_migemo_mode) {
  case MIGEMO_MODE_DISABLED: break;
  case MIGEMO_MODE_CMIGEMO:
#ifdef USE_MIGEMO
    if (_conf && _migemo_obj) {
      const Glib::ustring &encoding = *_conf->migemo()->encoding();
      unsigned char *re = NULL;
      if (encoding.empty()) {
        re = ::migemo_query(_migemo_obj, (const unsigned char *)str.c_str());
      } else {
        std::string estr = Glib::convert(str, encoding, "UTF-8");
        re = ::migemo_query(_migemo_obj, (const unsigned char *)estr.c_str());
      }
      if (re) {
        if (encoding.empty()) {
          res = (char *)re;
        } else {
          res = Glib::convert((char *)re, "UTF-8", encoding);
        }
        migemo_release(_migemo_obj, re);
      } else {
        log::err << "migemo: failed to query" << log::endl;
      }
    }
#endif
    break;
  case MIGEMO_MODE_MIGEMOSRV:
    if (_conf) {
      const Glib::ustring &encoding = *_conf->migemo()->encoding();
      URL url(*_conf->migemo()->url());
      url.add_path("query");
      if (encoding.empty()) {
        url.set_param("word", str);
      } else {
        std::string estr = Glib::convert(str, encoding, "UTF-8");
        url.set_param("word", estr);
      }
      url.set_param("op_mode", "perl");
      //log::dbg << url.to_string() << log::endl;

      try {
        res = loader::Pool::request_sync(url, encoding)->get_source().data();
      } catch(loader::RefError err) {
        log::err << "migemosrv: " << err->get_message() << log::endl;
        throw;
      }
      //log::dbg << "migemosrv: " << res << log::endl;
    }
    break;
  }
  return res;
}

void Search::recreate_re() {
  if (str_.empty()) {
    if (re_) {
      re_.reset();
      signal_changed_();
    }
  } else {
    try {
      switch(*type_) {
      case SEARCHTYPE_MIGEMO:
        if (migemo_usable()) {
          try {
            Glib::ustring re = migemo_query(str_);
            if (re.empty()) {
              re_.reset();
            } else {
              re_ = Regex::create(re, true);
            }
          } catch(const Glib::ConvertError &ex) {
            log::err << "migemo: " << ex.what() << log::endl;
            re_.reset();
          } catch(...) {
            log::err << "migemo: failed to query" << log::endl;
            re_.reset();
          }
          break;
        }
      case SEARCHTYPE_NORMAL:
        re_ = normal_re::create(str_);
        break;
      case SEARCHTYPE_REGEX:
        re_ = Regex::create(str_, true);
        break;
      }
    } catch(...) {
      log::err << "search: unknown error" << log::endl;
      re_.reset();
    }
    signal_changed_();
  }
}

void Search::set(const Glib::ustring &str) {
  if (str != str_) {
    str_ = str;
    recreate_re();
  }
}

void Search::convert_to_regex() {
  if (*type_ == SEARCHTYPE_REGEX) return;
  if (!re_) return;
  str_ = re_->get_source();
  type_ = SEARCHTYPE_REGEX;
}

Search::Search(const Glib::ustring &name) :
  conf::Element(name),
  type_        ("type", SEARCHTYPE_NORMAL)
{
  add(type_);

  type_->signal_changed()
    .connect(sigc::mem_fun(*this, &Search::recreate_re));
}

}/*gpyp*/
